<?php


namespace Xianlin\Validation;

use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;

/**
 * 扩展验证器
 */
class Validate {

    /**
     * 自定义字段含义
     * @var array
     */
    protected $customAttributes = [];

    /**
     * 当前验证规则
     * @var array
     */
    protected $rule = [];

    /**
     * 验证提示信息
     * @var array
     */
    protected $message = [];

    /**
     * 验证场景定义
     * @var array
     */
    protected $scene = [];

    /**
     * 设置当前验证场景
     * @var array
     */
    protected $currentScene = null;

    /**
     * 验证失败错误信息
     * @var array
     */
    protected $error = [];

    /**
     * 场景需要验证的规则
     * @var array
     */
    protected $only = [];

    /**
     * 场景下规则别名
     * @var array
     */
    protected $sceneAlisRule = [];

    public function __construct()
    {
        $this->autoLoadValidatorRule();
    }

    /**
     * 自动加载验证器里的方法当做验证规则
     * 方法名以 validate 开头
     * */
    private function autoLoadValidatorRule()
    {
        $called_class=get_called_class();
        $called_class_methods=get_class_methods($called_class);
        foreach ($called_class_methods as $method){
            $is_validate=substr($method, 0,8);
            $rule = Str::snake(substr($method, 8));
            if ($is_validate=='validate' && !empty($rule)){
                Validator::extend($rule, $called_class."@".$method);
            }
        }
    }


    /**
     * 设置验证场景
     * @access public
     * @param string $name 场景名
     * @return $this
     */
    public function scene(string $name): BaseValidate
    {
        // 设置当前场景
        $this->currentScene = $name;
        return $this;
    }

    /**
     * 检测所有客户端发来的参数是否符合验证类规则
     * @throws
     * @return true
     */
    public function goCheck(): bool
    {
        //必须设置contetn-type:application/json
        if (!$this->check()) {
            $this->sendFailResponse((is_array($this->error) ? implode(';', $this->error) : $this->error));
        }
        return true;
    }

    /**
     * 数据验证
     * @access public
     * @param array $data 数据
     * @param mixed $rules 验证规则
     * @param array $message 自定义验证信息
     * @param string $scene 验证场景
     * @param array $customAttributes
     * @return bool
     */
    public function check(array $data=[], $rules = [], array $message = [], string $scene = '', array $customAttributes=[]): bool
    {
        $this->error =[];
        if (empty($data)) {
            $data = request()->all();
        }
        if (empty($rules)) {
            //读取验证规则
            $rules = $this->rule;
        }
        if (empty($message)) {
            $message = $this->message;
        }

        if (empty($customAttributes)) {
            $customAttributes = $this->customAttributes;
        }

        //读取场景
        if (!$this->getScene($scene)) {
            return false;
        }

        //如果场景需要验证的规则不为空
        if (!empty($this->only)) {
            $new_rules = [];
            foreach ($this->only as $key => $value) {
                //首先，取规则里的
                if (array_key_exists($value,$rules)) {
                    $new_rules[$value] = $rules[$value];
                }elseif(isset($this->sceneAlisRule[$value])){
                    //其次匹配场景别名规则
                    if (!isset($this->sceneAlisRule[$value]['alis_rule_key']) || !isset($this->sceneAlisRule[$value]['alis_rule_val']))
                    {
                        $this->sendFailResponse(sprintf('%s 规则别名配置错误', $value));
                    }
                    $rule_key = $this->sceneAlisRule[$value]['alis_rule_key'];
                    $rule_val = $this->sceneAlisRule[$value]['alis_rule_val'];
                    $new_rules[$rule_key] = $rule_val;
                }

            }
            $rules = $new_rules;
        }
        //dd($rules);
        $validator = Validator::make($data, $rules, $message, $customAttributes);
        //验证失败
        if ($validator->fails()) {
            $this->error = $validator->errors()->first();
            return false;
        }

        return empty($this->error);
    }

    /**
     * 获取数据验证的场景
     * @access protected
     * @param string $scene 验证场景
     * @return bool
     */
    protected function getScene(string $scene = ''): bool
    {
        if (empty($scene)) {
            // 读取指定场景
            $scene = $this->currentScene;
        }
        $this->only = [];

        if (empty($scene)) {
            return true;
        }

        if (!isset($this->scene[$scene])) {
            //指定场景未找到写入error
            $this->error = "scene:".$scene.'is not found';
            return false;
        }
        // 如果设置了验证适用场景
        $scene = $this->scene[$scene];
        if (is_string($scene)) {
            $scene = explode(',', $scene);
        }
        //将场景需要验证的字段填充入only
        $this->only = $scene;
        return true;
    }

    /**
     * 返回失败json数据
     * @param string $message
     * @param int $code
     */
    public function sendFailResponse(string $message = '', int $code = 1000){
        response()->json(['code' => $code, 'msg' => $message])->send();exit();
    }

    /**
     * @return array
     */
    public function getError(): array
    {
        return $this->error;
    }
}